﻿using LxBasic;

namespace LxCore
{
    public class ScalarBase : ZoneHead, IZoneField<double>
    {
        public double[] InnX;//场
        public double[] BouX;
        public Vector3d[] Grad;//紧跟X出现，有X则接着更新Grad
        protected IField<double> IniValue;
        public bool IsXInit { get; set; } = false;
        public bool IsGradInit { get; set; } = false;
        public override bool IsInitialized => IsXInit && IsGradInit;

        public override void CheckCreate(FvZone zone)
        {
            base.CheckCreate(zone);
            bool newInnX = CheckCreator.ArrayStrict(ref InnX, InnCells.Length);
            bool newBouX = CheckCreator.ArrayStrict(ref BouX, BouFaces.Length);
            if (newInnX || newBouX)
            {
                IsXInit = false;
                IsGradInit = false;
            }
            bool newGrad = CheckCreator.ArrayStrict(ref Grad, InnCells.Length);
            if (newGrad)
            {
                IsGradInit = false;
            }
        }
        public override void Reuse(ZoneHead oldOne)
        {
            base.Reuse(oldOne);
            if (oldOne is ScalarBase scalar)
            {
                this.InnX = scalar.InnX;
                this.BouX = scalar.BouX;
                this.Grad = scalar.Grad;
                this.IsXInit = scalar.IsXInit;
                this.IsGradInit = false;//重新计算吧
            }
        }
        public virtual void UpdateBoundsValue(ThreadInfo threadInfo)
        {
            throw new NotImplementedException();
        }

        public void SetXs(IField<double> xField, IIndexHost indexHost, ThreadInfo threadInfo)
        {
            (int i1, int endp1) = indexHost.CellIdx.Split(threadInfo);
            switch (indexHost)
            {
                case FvInner: setx(InnX); break;
                case FvBound: setx(BouX); break;
                default: throw new InvalidDataException();
            }
            void setx(double[] xArr)
            {
                if (xField.IsEqual0())
                {
                    Array.Clear(xArr, i1, endp1 - i1);
                }
                else if (xField.IsParameter(out double c))
                {
                    Array.Fill(xArr, c, i1, endp1 - i1);
                }
                else
                {
                    var xf = xField.GetCellVarArr(indexHost);
                    for (; i1 < endp1; i1++)
                    {
                        xArr[i1] = xf.GetCellVar(i1);
                    }
                }
            }
        }
        public void CalGradient_LSQ(ThreadInfo threadInfo)
        {
            (int i1, int endp1) = Zone.Inner.CellIdx.Split(threadInfo);
            for (; i1 < endp1; i1++)
            {
                Grad[i1] = InnCells[i1].CalGradient_LeastSquare(InnFaces, BouFaces, InnX, BouX);
            }
        }

        protected void predict_bound_byCGrad(FvBound bound, ThreadInfo threadInfo)//方法1，精度一般，计算量小，对应P603
        {
            (int i1, int endp1) = bound.FaceIdx.Split(threadInfo);
            for (; i1 < endp1; i1++)
            {
                ref var face = ref BouFaces[i1];
                BouX[i1] = InnX[face.OSideCell] + Vector3d.DotProduct(Grad[face.OSideCell], face.OToN);
            }
        }
        #region Limit
        private bool _IsEnableLimit = false;
        private double _MinLim;
        private double _MaxLim;
        public bool IsEnableLimit { get => _IsEnableLimit; }
        public double MinLim { get => _MinLim; }
        public double MaxLim { get => _MaxLim; }
        public bool IsLimitMin { get => MinLim != double.NegativeInfinity; }
        public bool IsLimitMax { get => MaxLim != double.PositiveInfinity; }
        public void SetLimitRule(bool isEnable, bool isLimMin, double minLim, bool isLimMax, double maxLim)
        {
            _IsEnableLimit = isEnable;
            _MinLim = isLimMin ? minLim : double.NegativeInfinity;
            _MaxLim = isLimMax ? maxLim : double.PositiveInfinity;
        }
        private unsafe void limit3(double[] d, IndexRange indexRange, ThreadInfo threadInfo)
        {
            (int i1, int endp1) = indexRange.Split(threadInfo);
            int NaN = 0, ExceedMax = 0, ExceedMin = 0;
            fixed (double* fixedptr = d)
            {
                double* ptr_i1 = fixedptr + i1;
                double* ptr_end = fixedptr + endp1;
                switch (IsLimitMax, IsLimitMin)
                {
                    case (true, true):
                        for (; ptr_i1 < ptr_end; ++ptr_i1)
                        {
                            if (double.IsNaN(*ptr_i1)) { NaN++; }
                        }
                        break;
                    case (true, false):
                        for (; ptr_i1 < ptr_end; ++ptr_i1)
                        {
                            if (double.IsNaN(*ptr_i1)) { NaN++; }
                            else if (*ptr_i1 > MaxLim) { *ptr_i1 = MaxLim; ExceedMax++; }
                        }
                        break;
                    case (false, true):
                        for (; ptr_i1 < ptr_end; ++ptr_i1)
                        {
                            if (double.IsNaN(*ptr_i1)) { NaN++; }
                            else if (*ptr_i1 < MinLim) { *ptr_i1 = MinLim; ExceedMin++; }
                        }
                        break;
                    case (false, false):
                        for (; ptr_i1 < ptr_end; ++ptr_i1)
                        {
                            if (double.IsNaN(*ptr_i1)) { NaN++; }
                            else if (*ptr_i1 < MinLim) { *ptr_i1 = MinLim; ExceedMin++; }
                            else if (*ptr_i1 > MaxLim) { *ptr_i1 = MaxLim; ExceedMax++; }
                        }
                        break;
                }
            }

            if (NaN > 0 || ExceedMin > 0 || ExceedMax > 0)
            {
                ThreadsMath.MemoryAdd((Float128)NaN, (Float128)ExceedMin, (Float128)ExceedMax);
            }
        }
        public void LimitScalarValue()
        {
            if (!IsEnableLimit) return;
            MultiThreads.RunParallelTask((i) =>
            {
                limit3(InnX, Zone.Inner.CellIdx, i);
                limit3(BouX, new IndexRange(Zone.BouFaces.Length), i);
            });
            (double nan, double exceedMin, double exceedMax) = ThreadsMath.MemoryRecallClear();
            int inan = (int)nan, imin = (int)exceedMin, imax = (int)exceedMax;
            if (inan > 0)
            {
                throw new Exception(string.Format($"区域{Zone.Name}出现NAN值，数量{inan}"));
            }
            if (imin > 0)
            {
                LxLog.Warn(string.Format($"区域{Zone.Name},{imin}个网格小于限制值{MinLim}"));
            }
            if (imax > 0)
            {
                LxLog.Warn(string.Format($"区域{Zone.Name},{imax}个网格大于限制值{MaxLim}"));
            }
        }


        #endregion
        public ICellVarArr<double> GetInnCellVarArr()
        {
            return new VarArrBuider.Direct<double>(InnX);
        }

        public ICellVarArr<double> GetBouCellVarArr()
        {
            return new VarArrBuider.Direct<double>(BouX);
        }

        public IFaceVarArr<double> GetInnFaceVarArr()
        {
            return new VarArrBuider.InnFaceByAve(this.Zone, InnX);
        }

        public IFaceVarArr<double> GetBouFaceVarArr()
        {
            return new VarArrBuider.Direct<double>(BouX);
        }
        public override void FromJsonKV(string key, IJsonReader jsReader)
        {
            switch (key)
            {
                //case nameof(IsInitialized): _IsInitialized = jsReader.ReadBool(); break;
                case nameof(InnX): jsReader.ReadDoubleArray(InnX, e => InnX = e); break;
                case nameof(BouX):
                    jsReader.ReadDoubleArray(BouX, e => BouX = e);
                    IsXInit = true;
                    break;
                default: base.FromJsonKV(key, jsReader); break;
            }
        }
        public override void ToJsonKV(IJsonWriter jsWriter)
        {
            base.ToJsonKV(jsWriter);
            if (IsXInit)
            {
                //jsWriter.WriteBool(nameof(IsInitialized), IsInitialized);
                jsWriter.WriteDoubleArray(nameof(InnX), InnX);
                jsWriter.WriteDoubleArray(nameof(BouX), BouX);
            }
        }
    }
}
//public void UpdateBoundVar_Grad0(int faceIdx)//对于flux为0的情况，前面设置好离散b=0，这里直接算
//{
//    ref var face = ref BouFaces[faceIdx];
//    ref double bv = ref BouX[faceIdx];
//    double svv = Vector3d.DotProduct(face.Normal, face.FaceNOrth) / InnCells[face.OSideCell].Volumn;
//    double Ab = svv + face.gT;
//    double Bb = -Vector3d.DotProduct(Grad[face.OSideCell], face.FaceNOrth) + svv * bv;
//    bv = (Bb + face.gT * InnX[face.OSideCell]) / Ab;
//}
//protected virtual void UpdateBoundVars_Grad0(FvBound bound, ThreadInfo threadInfo)
//{
//    (int i1, int endp1) = bound.FaceIdx.Split(threadInfo);
//    for (; i1 < endp1; i1++)
//    {
//        UpdateBoundVar_Grad0(i1);
//    }
//}
//protected void limit2(ThreadInfo threadInfo)
//{
//    (int i1, int endp1) = Zone.Inner.CellIdx.Split(threadInfo);
//    double[] d = this.XInn;
//    int NaN = 0, ExceedMax = 0, ExceedMin = 0;
//    switch (IsLimitMax, IsLimitMin)
//    {
//        case (true, true):
//            for (; i1 < endp1; ++i1)
//            {
//                if (double.IsNaN(d[i1])) { NaN++; }
//            }
//            break;
//        case (true, false):
//            for (; i1 < endp1; ++i1)
//            {
//                ref double xi = ref d[i1];
//                if (double.IsNaN(xi)) { NaN++; }
//                else if (xi > MaxLim) { xi = MaxLim; ExceedMax++; }
//            }
//            break;
//        case (false, true):
//            for (; i1 < endp1; ++i1)
//            {
//                ref double xi = ref d[i1];
//                if (double.IsNaN(xi)) { NaN++; }
//                else if (xi < MinLim) { xi = MinLim; ExceedMin++; }
//            }
//            break;
//        case (false, false):
//            for (; i1 < endp1; ++i1)
//            {
//                ref double xi = ref d[i1];
//                if (double.IsNaN(xi)) { NaN++; }
//                else if (xi < MinLim) { xi = MinLim; ExceedMin++; }
//                else if (xi > MaxLim) { xi = MaxLim; ExceedMax++; }
//            }
//            break;
//    }
//    if (NaN > 0 || ExceedMin > 0 || ExceedMax > 0)
//    {
//        ThreadsMath.MemoryAdd((Float128)NaN, (Float128)ExceedMin, (Float128)ExceedMax);
//    }
//}